/**
 * @author: dn-jinmin/dn-jinmin
 * @doc:
 */

package websocket

import (
	"github.com/gorilla/websocket"
	"net/http"
	"sync"
	"time"
)

// Conn 表示一个带有额外功能的 WebSocket 连接。
type Conn struct {
	idleMu sync.Mutex

	// Uid 用户唯一标识符
	Uid string

	// Conn 原始的 WebSocket 连接
	*websocket.Conn
	// s 服务器实例
	s *Server

	// idle 上次活动时间
	idle time.Time
	// maxConnectionIdle 最大空闲时间
	maxConnectionIdle time.Duration

	messageMu sync.Mutex
	// readMessage 存储已读取的消息队列
	readMessage []*Message
	// readMessageSeq 存储已读取的消息序列
	readMessageSeq map[string]*Message

	// message 消息通道
	message chan *Message

	// done 关闭信号通道
	done chan struct{}
}

// NewConn 创建一个新的 WebSocket 连接。
// 参数:
//
//	s: 服务器实例
//	w: HTTP 响应写入器
//	r: HTTP 请求
//
// 返回:
//
//	*Conn: 新创建的连接对象
func NewConn(s *Server, w http.ResponseWriter, r *http.Request) *Conn {
	c, err := s.upgrader.Upgrade(w, r, nil)
	if err != nil {
		s.Errorf("upgrade err %v", err)
		return nil
	}

	conn := &Conn{
		Conn:              c,
		s:                 s,
		idle:              time.Now(),
		maxConnectionIdle: s.opt.maxConnectionIdle,
		readMessage:       make([]*Message, 0, 2),
		readMessageSeq:    make(map[string]*Message, 2),
		message:           make(chan *Message, 1),
		done:              make(chan struct{}),
	}

	go conn.keepalive()
	return conn
}

// appendMsgMq 将消息添加到消息队列中。
// 参数:
//
//	msg: 要添加的消息
func (c *Conn) appendMsgMq(msg *Message) {
	c.messageMu.Lock()
	defer c.messageMu.Unlock()

	// 检查消息是否已经在读取队列中
	if m, ok := c.readMessageSeq[msg.Id]; ok {
		// 如果消息已经存在且已被确认
		if len(c.readMessage) == 0 {
			// 队列中没有该消息
			return
		}

		// 检查新的消息是否已经确认
		if m.AckSeq >= msg.AckSeq {
			// 没有进行确认, 重复
			return
		}

		c.readMessageSeq[msg.Id] = msg
		return
	}
	// 如果消息尚未确认，避免客户端重复发送多余的确认消息
	if msg.FrameType == FrameAck {
		return
	}

	c.readMessage = append(c.readMessage, msg)
	c.readMessageSeq[msg.Id] = msg
}

// ReadMessage 从 WebSocket 连接中读取消息。
// 返回:
//
//	messageType: 消息类型
//	p: 消息数据
//	err: 错误信息
func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
	messageType, p, err = c.Conn.ReadMessage()

	c.idleMu.Lock()
	defer c.idleMu.Unlock()
	c.idle = time.Time{}
	return
}

// WriteMessage 向 WebSocket 连接中写入消息。
// 参数:
//
//	messageType: 消息类型
//	data: 消息数据
//
// 返回:
//
//	err: 错误信息
func (c *Conn) WriteMessage(messageType int, data []byte) error {
	c.idleMu.Lock()
	defer c.idleMu.Unlock()
	// 方法是并不安全
	err := c.Conn.WriteMessage(messageType, data)
	c.idle = time.Now()
	return err
}

// Close 关闭 WebSocket 连接。
// 返回:
//
//	err: 错误信息
func (c *Conn) Close() error {
	select {
	case <-c.done:
	default:
		close(c.done)
	}

	return c.Conn.Close()
}

// keepalive 保持连接活跃，防止因长时间空闲而被关闭。
func (c *Conn) keepalive() {
	idleTimer := time.NewTimer(c.maxConnectionIdle)
	defer func() {
		idleTimer.Stop()
	}()

	for {
		select {
		case <-idleTimer.C:
			c.idleMu.Lock()
			idle := c.idle
			if idle.IsZero() { // 连接处于活跃状态
				c.idleMu.Unlock()
				idleTimer.Reset(c.maxConnectionIdle)
				continue
			}
			val := c.maxConnectionIdle - time.Since(idle)
			c.idleMu.Unlock()
			if val <= 0 {
				// 连接空闲时间超过最大空闲时间，优雅地关闭连接
				c.s.Close(c)
				return
			}
			idleTimer.Reset(val)
		case <-c.done:
			return
		}
	}
}
